/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if !defined(___GRAPH2D_COMPONENT_H___)
#define ___GRAPH2D_COMPONENT_H___

#include "object.h"

#include "cellAnimation.h"
#include "data.h"
#include "texture.h"
#include "vector2.h"

#include <cstddef>
#include <map>
#include <stack>
#include <string>

namespace Graph2D
{
	class Component;
	class Container;
	class Layout;
	class Scene;

	////////////////////////////////////////////////////////////////////////////////////////////////
	//! フレームのスタイル
	typedef enum FrameStyle
	{
		FRAME_STYLE_NORMAL = 0x00,			//!< 通常スタイル
		FRAME_STYLE_PUSHED = 0x01,			//!< ボタンが押されたスタイル
		FRAME_STYLE_NOLINE = 0x02,			//!< 枠線なし
		FRAME_STYLE_FLAT   = 0x04,			//!< フラットシェード
		FRAME_STYLE_CUSTOM = 0x10,			//!< スタイル無効（何も処理しない）
	}FrameStyle;

	////////////////////////////////////////////////////////////////////////////////////////////////
	//! テキストアライメント 水平
	typedef enum TextAlignmentH
	{
		TEXT_ALIGNMENT_LEFT,				//!< 左づめ
		TEXT_ALIGNMENT_CENTER,				//!< 中央揃え
		TEXT_ALIGNMENT_RIGHT,				//!< 右づめ
	}TextAlignmentH;

	////////////////////////////////////////////////////////////////////////////////////////////////
	//! テキストアライメント 垂直
	typedef enum TextAlignmentV
	{
		TEXT_ALIGNMENT_TOP,					//!< 上づめ
		TEXT_ALIGNMENT_MIDDLE,				//!< 中央揃え
		TEXT_ALIGNMENT_BOTTOM,				//!< 下づめ
	}TextAlignmentV;

	////////////////////////////////////////////////////////////////////////////////////////////////
	class ActionEvent
	{
	public:
		virtual void onAction(Component* component, const unsigned int identification) = 0;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	class ClickEvent
	{
	public:
		virtual void onClick(Component* component) = 0;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	class MouseEvent
	{
		virtual bool onTouchesBegan(const Vector2& localPosition) = 0;
		virtual bool onTouchesEnded(const Vector2& localPosition) = 0;
		virtual bool onTouchesMoved(const Vector2& localPosition) = 0;
		virtual bool onTouchesCancelled(const Vector2& localPosition) = 0;
	};

	////////////////////////////////////////////////////////////////////////////////////////////////
	class Component : public Object, public MouseEvent
	{
		DISALLOW_COPY_AND_ASSIGN(Component);

		typedef Object super;

	public:
		Component();									//!< コンストラクタ
		virtual ~Component();							//!< デストラクタ

		////////////////////////////////////////////////////////////////////////////////////////////
		virtual const char* getTypeName() const			//!< タイプ名を取得します
		{
			return "Component";
		}

		unsigned char getFrameStyle() const				//!< フレームスタイルを取得します
		{
			return frameStyle;
		}

		void setFrameStyle(const unsigned char style)	//!< フレームスタイルを設定します
		{
			this->frameStyle = style;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		Container* getParent() const					//!< 親コンポーネントを取得します
		{
			return parent;
		}

		/*!
		 * 親コンポーネントを設定します
		 * @param[in]	container	親コンテナオブジェクトのポインター
		 */
		void setParent(Container* container)
		{
			parent = container;
		}

		void removeFromParent();						//!< 親から開放します

		Scene* getParentScene() const;

		////////////////////////////////////////////////////////////////////////////////////////////
		void applyLayout(Layout* layout);

		////////////////////////////////////////////////////////////////////////////////////////////
		Container* forcusScrollableContainer(const Vector2& localPosition);
		Container* getScrollableContainer();
		const Container* getScrollableContainer() const
		{
			return getScrollableContainer();
		}



		////////////////////////////////////////////////////////////////////////////////////////////
		Vector2 getScreenPosition() const;				//!< スクリーン座標を取得します
		Vector2 translatePosition(const Vector2& screenPosition) const;	//!< スクリーン座標をローカル座標に変換します

		Vector2 getCenterPosition() const				//!< ローカル中心座標を取得します
		{
			return getPosition() + getSize() * 0.5f;
		}

		Vector2 getLeftTopPosition() const				//!< スケールを加味した左上座標を取得します
		{
			return getCenterPosition() - getSize() * 0.5f * getScale();
		}

		Vector2 getRightBottom() const					//!< スケールを加味した右下座標を取得します
		{
			return getCenterPosition() + getSize() * 0.5f * getScale();
		}

		Vector2 getBottomPosition() const				//!< ローカル足元座標を取得します
		{
			return getPosition() + getSize() * Vector2(0.5f, 1.0f);
		}

		void setBottomPosition(const Vector2& position);//!< ローカル足元座標を設定します
		void setSize(const Vector2& size);
		void setSize(const float w, const float h);		//!< サイズを設定します

		bool isInScreenPosition(const Vector2& position) const	//!< 指定座標を含むか否か
		{
#if 0
			const Vector2 localPosition = screenPosition - getScreenPosition();
			const Vector2& localSize = getSize();
			return (localPosition.x >= 0) && (localPosition.y >= 0) && (localPosition.x < localSize.x) && (localPosition.y < localSize.y);
#else
			((void)position);
			return false;
#endif
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		ActionEvent* getActionEvent()
		{
			return actionEvent;
		}

		void setActionEvent(ActionEvent* actionEvent)
		{
			this->actionEvent = actionEvent;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		Component* setMouseCapture()								//!< マウスキャプチャー状態にする
		{
			return setMouseCapture(this);
		}
		bool releaseMouseCapture()
		{
			return releaseMouseCapture(this);
		}
		bool isMouseCaptured() const;								//!< マウスキャプチャー中？
		static Component* setMouseCapture(Component* component);	//!< マウスキャプチャー状態にする
		static bool releaseMouseCapture(Component* component);		//!< マウスキャプチャー状態を解除する
		static Component* getMouseCapture();						//!< マウスキャプチャーしているコンポーネントを取得する

		////////////////////////////////////////////////////////////////////////////////////////////
		static Component* deserialize(mana_stream* stream);			//!< デシリアライズ

		////////////////////////////////////////////////////////////////////////////////////////////
		virtual void onSerialize(mana_stream* stream) const;		//!< シリアライズイベント
		virtual void onDeserialize(mana_stream* stream);			//!< デシリアライズイベント
		virtual void onDestroy(Component* component)				//!< 削除イベント
		{
			(void)component;
		}
		virtual void onUpdate(const UpdateInfomation& updateInfomation);	//! 更新イベント
		virtual bool onDraw(const DrawRect& drawRect);				//!< 描画イベント
		virtual bool onTouchesBegan(const Vector2& localPosition);	//!< タッチ開始イベント
		virtual bool onTouchesMoved(const Vector2& localPosition);	//!< タッチ移動イベント
		virtual bool onTouchesEnded(const Vector2& localPosition);	//!< タッチ終了イベント
		virtual bool onTouchesCancelled(const Vector2& localPosition);	//!< タッチキャンセルイベント
		virtual bool onSize(const Vector2& size);					//!< サイズ変更イベント
		virtual void onSizeChanged();								//!< サイズ変更イベント

		////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool touchesBegan(const Vector2& localPosition);	//!< タッチ開始
		virtual bool touchesMoved(const Vector2& localPosition);	//!< タッチ移動
		virtual bool touchesEnded(const Vector2& localPosition);	//!< タッチ終了
		virtual bool touchesCancelled(const Vector2& localPosition);//!< タッチキャンセル

		////////////////////////////////////////////////////////////////////////////////////////////
		virtual bool compare(const Component& component) const;
		virtual bool operator==(const Component& component) const
		{
			return compare(component);
		}

		virtual bool operator!=(const Component& component) const
		{
			return !compare(component);
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		static void panel(const Vector2& origin, const Vector2& size, const Color& color, const unsigned int style);

		static void clearAll();							//!< 全てのコンポーネントを強制的に開放します
		static void dumpAll();							//!< 全てのコンポーネントをダンプします
		static Component* find(const unsigned int identification);	//!< コンポーネントを検索します
		static void clearLastTouchedComponentID()		//!< 最後にタッチされたコンポーネントIDを消去します
		{
			lastTouchedComponentID = 0;
		}

		static unsigned int getLastTouchedComponentID()	//!< 最後にタッチされたコンポーネントIDを取得します
		{
			return lastTouchedComponentID;
		}

		static bool getDebugInfomation()				//!< デバック情報フラグを取得します
		{
			return drawDebugInfomation;
		}

		static void setDebugInfomation(const bool enable)	//!< デバック情報フラグを設定します
		{
			drawDebugInfomation = enable;
		}

		static bool isDragging()
		{
			return dragging;
		}
		static unsigned int getLastTouchedID()			//!< 最後にタッチされたコンポーネントIDを取得します
		{
			return lastTouchedComponentID;
		}

		// テスト
		virtual void testSerialize() const;		//!< シリアライズテスト
		static void allTestSerialize();			//!< シリアライズテスト

	protected:
		virtual Color getDrawColor(const DrawRect& drawRect, const Color& color) const;

		/*!
		 * タッチ開始座標
		 */
		const Vector2& getToucheBeganPosition() const
		{
			return toucheBeganPosition;
		}

	private:
		void removeFromComponents();

	private:
		Container* parent;						//!< 親コンポーネント
		ActionEvent* actionEvent;

		Vector2 toucheBeganPosition;			//!< タッチ開始座標

		float enableColor;						//!< 有効・無効変更時の輝度値


		////////////////////////////////////////////////////////////////////////////////////////////
		// スタティック変数
	private:
		static std::map<unsigned int, Component*> components;
		static std::stack<Component*> mouseCaptureComponent;
		static Component* lastFoundComponent;
		static unsigned int lastTouchedComponentID;

	protected:
		static bool drawDebugInfomation;
		static bool dragging;



	private:
		// 検討中
		unsigned char frameStyle;				//!< フレームのスタイル

		Vector2 textureScrollOffset;
		Vector2 textureScrollSpeed;

		Vector2 animationCellStartPosition;		//!< セルアニメーション開始位置
		Vector2 animationCellSize;				//!< セルアニメーションサイズ
		float animationCellTime;				//!< セルアニメーション時間
		float animationCellWait;				//!< セルアニメーションウェイト
		unsigned short animationCellCount;		//!< セルアニメーション枚数
		unsigned short animationCellMaxCount;	//!< セルアニメーション枚数
		short animationCellLoopCountOrigin;
		short animationCellLoopCount;			//!< セルアニメーション再生回数（負なら無限）
		bool animationCellEnable:8;				//!< セルアニメーションフラグ
		bool animationCellRunning:8;			//!< セルアニメーション実行フラグ

		std::map<std::string, CellAnimation*> animations;	//!< セルアニメーション情報
	};
}

#endif
